package net.chasing.androidbaseconfig.widget.rich;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.BackgroundColorSpan;
import android.text.style.ClickableSpan;
import android.text.style.ForegroundColorSpan;
import android.text.style.URLSpan;
import android.text.util.Linkify;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatTextView;

import net.chasing.androidbaseconfig.widget.rich.base.CustomUrlClickSpan;
import net.chasing.androidbaseconfig.widget.rich.base.MyCenterImageSpan;
import net.chasing.androidbaseconfig.widget.rich.base.NoUnderlineSpan;
import net.chasing.androidbaseconfig.widget.rich.base.OnClickRichTextListener;
import net.chasing.androidbaseconfig.widget.rich.base.RichParserManager;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 添加了富文本解析编辑的TextView
 * Url的解析是保持开启的
 * 默认不进行其它（@）解析，避免影响效率
 * 通过{@link RichTextView#setRichParseEnable(boolean isParserRichText)};开启解析@标签等
 * 注意：
 * 因为在onDraw进行了maxLines的判断，重新设置了文本，所以如果重新切换maxLines为更大值，也不会再显示后面的文本了
 */
public class RichTextView extends AppCompatTextView {
    public static boolean IsLongClick = false;
    private boolean enableParseRichText = false;
    private CustomUrlClickSpan.OnUrlClickListener mOnUrlClickListener;
    private boolean enableClickUrl = true;
    private boolean enableParseUrl = true;
    private boolean haveLongClickListener;

    protected void setOnUrlClickListener(CustomUrlClickSpan.OnUrlClickListener onUrlClickListener) {
        mOnUrlClickListener = onUrlClickListener;
    }

    public RichTextView(Context context) {
        this(context, null);
    }

    public RichTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        setHighlightColor(0xffffffff);
        setAutoLinkMask(Linkify.WEB_URLS); //启用Url解析
        setMovementMethod(MyLinkedMovementMethod.getInstance()); //设置之后ClickableSpan等url的点击事件才能生效
    }

    public void setUrlEnable(boolean enable) {
        enableParseUrl = enable;
        if (enable) {
            setAutoLinkMask(Linkify.WEB_URLS); //启用Url解析
            setMovementMethod(MyLinkedMovementMethod.getInstance());
        } else if (enableParseRichText) {
            setAutoLinkMask(0); //取消解析
            setMovementMethod(MyLinkedMovementMethod.getInstance());
        } else {
            setAutoLinkMask(0);
            setMovementMethod(null);
        }
    }

    public void setRichParseEnable(boolean isParserRichText) {
        enableParseRichText = isParserRichText;
        if (enableParseRichText || enableParseUrl)
            setMovementMethod(MyLinkedMovementMethod.getInstance());
        else
            setMovementMethod(null);
    }

    public void setRichText(SpannableString richText) {
        if (richText != null) {
            setText(richText);
            setLink();
        } else
            setText("");
    }

    public void setRichText(Spanned spannedText) {
        if (spannedText != null) {
            setText(spannedText);
            setLink();
        } else
            setText("");
    }

    public void setRichText(String richText) {
        if (!TextUtils.isEmpty(richText)) {
            setText(richText);
            setLink();
        } else
            setText("");
    }

    public void setRichText(CharSequence richText) {
        if (!TextUtils.isEmpty(richText)) {
            setText(richText);
            setLink();
        } else
            setText("");
    }

    //查询并更改富文本相应样式
    protected void setLink() {
        scrollTo(0, 0);

        CharSequence charText = getText();
        if (charText instanceof Spannable) {
            if (enableParseRichText) {
                setText(RichParserManager.getInstance().parseRichItems(getContext(), getText().toString()));
            }
            checkWebUrl();
        }
    }

    //重写以限制不能滚动
    @Override
    protected void onScrollChanged(int horiz, int vert, int oldHoriz, int oldVert) {
        super.onScrollChanged(horiz, vert, oldHoriz, oldVert);
        scrollTo(0, 0);
    }

    //检测Url
    private void checkWebUrl() {
        CharSequence charText = getText();
        if (enableParseUrl && charText instanceof Spannable) {
            int end = charText.length();
            Spannable spannable = (Spannable) charText;
            URLSpan[] urlSpans = spannable.getSpans(0, end, URLSpan.class);
            if (urlSpans.length > 0) { //有超链接
                CustomUrlClickSpan[] spans = spannable.getSpans(0, end, CustomUrlClickSpan.class);
                if (spans != null && spans.length > 0) {
                    for (CustomUrlClickSpan span : spans) {
                        ((Spannable) charText).removeSpan(span);
                    }
                }
                SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(charText);
                // 循环遍历并拦截 所有链接
                for (URLSpan uri : urlSpans) {
                    String url = uri.getURL();
                    if (url.toLowerCase().startsWith("http://") || url.toLowerCase().startsWith("https://") || url.toLowerCase().startsWith("www.")) {
                        CustomUrlClickSpan customUrlSpan = new CustomUrlClickSpan(url, enableClickUrl, RichParserManager.RICH_TEXT_COLOR);
                        customUrlSpan.setOnUrlClickListener(mOnUrlClickListener);
                        spannableStringBuilder.setSpan(customUrlSpan, spannable.getSpanStart(uri),
                                spannable.getSpanEnd(uri), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
                    }
                }
                setText(spannableStringBuilder);
                deleteUnderLine();
            }
        }
    }

    private void deleteUnderLine() {
        CharSequence _text = getText();
        //设置超链接无下划线
        if (_text instanceof Spannable) {
            Spannable sp = (Spannable) _text;
            NoUnderlineSpan[] noUnderlineSpans = sp.getSpans(0, _text.length(), NoUnderlineSpan.class);
            if (noUnderlineSpans != null && noUnderlineSpans.length > 0) {
                for (NoUnderlineSpan span : noUnderlineSpans) {
                    sp.removeSpan(span);
                }
            }
            NoUnderlineSpan mNoUnderlineSpan = new NoUnderlineSpan();
            sp.setSpan(mNoUnderlineSpan, 0, sp.length(), Spanned.SPAN_MARK_MARK);
        }
    }

    private SpannableStringBuilder mSpannableStringBuilder;

    @Override
    protected void onDraw(Canvas canvas) {
        if (getLineCount() > getMaxLines()) { //当需要显示的行数大于最大显示行数时
            if (getText().length() > getLayout().getLineVisibleEnd(getMaxLines() - 1)) { //当最大行数的最后一个字符位置小于文本长度时，则需要添加...
                int lastIndex = getLayout().getLineVisibleEnd(getMaxLines() - 1) - 2;
                if (lastIndex > 0) {
                    if (mSpannableStringBuilder == null)
                        mSpannableStringBuilder = new SpannableStringBuilder();
                    else
                        mSpannableStringBuilder.clear();
                    //文本裁剪，减去最后一行的2个字符，替换成...
                    mSpannableStringBuilder.append(getText().subSequence(0, lastIndex)).append("...");
                    //注意：因为重新设置了文本，所以如果重新切换maxLines为更大值，也不会再显示后面的文本了
                    setAutoLinkMask(0); //取消Url解析
                    setText(mSpannableStringBuilder);
                    if (enableParseUrl)
                        setAutoLinkMask(Linkify.WEB_URLS); //启用Url解析
                }
            }
        }
        super.onDraw(canvas);
    }

    @Override
    public boolean performLongClick() {
        IsLongClick = true;
        return super.performLongClick();
    }

    @Override
    public void setOnLongClickListener(@Nullable OnLongClickListener l) {
        super.setOnLongClickListener(l);
        haveLongClickListener = l != null;
    }

    public static boolean isLongClick() {
        return IsLongClick;
    }

    public static void setIsLongClick(boolean isLongClick) {
        IsLongClick = isLongClick;
    }

    public void setEnableClickUrl(boolean enableClickUrl) {
        this.enableClickUrl = enableClickUrl;
    }

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_UP) {
            if (haveLongClickListener && IsLongClick) {
                IsLongClick = false;
                return true;
            }
        }
        return super.onTouchEvent(event);
    }

    // region 添加文字或图标，避免之前的富文本失效
    public void addRichText(String richText, final int color) {
        addRichText(richText, color, null);
    }

    public void addRichText(String richText, final int color, final OnClickRichTextListener onClickRichTextListener) {
        addRichText(false, richText, 0, color, false, onClickRichTextListener);
    }

    public void addRichTextBefore(String richText, int color) {
        addRichTextBefore(richText, color, null);
    }

    public void addRichTextBefore(String richText, final int color, final OnClickRichTextListener onClickRichTextListener) {
        addRichText(true, richText, 0, color, false, onClickRichTextListener);
    }

    public void addRichTextBefore(String richText, int color, int textSize) {
        addRichText(true, richText, textSize, color, false, null);
    }

    /**
     * 在文本中添加富文本
     *
     * @param isAddBefore             是否在文本最前添加
     * @param richText                要显示的富文本
     * @param color                   富文本颜色
     * @param needDeleteLine          是否需要显示删除线
     * @param onClickRichTextListener 富文本点击事件
     */
    public void addRichText(boolean isAddBefore, String richText, int textSizePx, final int color,
                            final boolean needDeleteLine, final OnClickRichTextListener onClickRichTextListener) {
        addRichText(isAddBefore, richText, textSizePx, color, false, needDeleteLine, onClickRichTextListener);
    }

    /**
     * 在文本中添加富文本
     *
     * @param isAddBefore             是否在文本最前添加
     * @param richText                要显示的富文本
     * @param textSizePx              字体大小，单位px
     * @param color                   富文本颜色
     * @param isBoldStyle             是否粗体
     * @param needDeleteLine          是否需要显示删除线
     * @param onClickRichTextListener 富文本点击事件
     */
    public void addRichText(boolean isAddBefore, String richText, int textSizePx, final int color, final boolean isBoldStyle,
                            final boolean needDeleteLine, final OnClickRichTextListener onClickRichTextListener) {
        if (TextUtils.isEmpty(richText)) return;
        SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(getText());
        SpannableStringBuilder addText = RichParserManager.getInstance().parseRichItems(getContext(), richText);
        ClickableSpan clickableSpan = new ClickableSpan() {
            @Override
            public void updateDrawState(TextPaint ds) {
                ds.setColor(color);
                if (needDeleteLine)
                    ds.setFlags(Paint.STRIKE_THRU_TEXT_FLAG);
                ds.setFakeBoldText(isBoldStyle);
            }

            @Override
            public void onClick(@NonNull View view) {
                if (RichTextView.isLongClick()) {
                    RichTextView.setIsLongClick(false);
                    return;
                }
                if (onClickRichTextListener != null)
                    onClickRichTextListener.onClick();
            }
        };
        addText.setSpan(clickableSpan, 0, addText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        if (textSizePx != 0) {
            AbsoluteSizeSpan sizeSpan = new AbsoluteSizeSpan(textSizePx);
            addText.setSpan(sizeSpan, 0, addText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        }
        if (isAddBefore)
            spannableStringBuilder.insert(0, addText);
        else
            spannableStringBuilder.append(addText);
        setText(spannableStringBuilder);
        checkWebUrl();
    }

    public void addImgBefore(int imgRes) {
        addImgBefore(imgRes, 1);
    }

    public void addImgBefore(int imgRes, int spaceCount) {
        addImg(true, imgRes, spaceCount);
    }

    public void addImgAfter(int imgRes, int spaceCount) {
        addImg(false, imgRes, spaceCount);
    }

    //在text前/后添加图标
    private void addImg(boolean isBefore, int imgRes, int spaceCount) {
        SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(getText());
        StringBuilder space = new StringBuilder(" ");
        if (spaceCount > 0) {
            space.delete(0, space.length());
            for (int i = 0; i < spaceCount + 1; i++)
                space.append(" ");
        }
        SpannableString spannableString = new SpannableString(space.toString());
        MyCenterImageSpan imageSpan = new MyCenterImageSpan(getContext(), imgRes);
        if (isBefore) {
            spannableString.setSpan(imageSpan, spaceCount - 1, spaceCount, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            spannableStringBuilder.insert(0, spannableString);
        } else {
            spannableString.setSpan(imageSpan, spannableString.length() - 1, spannableString.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            spannableStringBuilder.append(spannableString);
        }
        setText(spannableStringBuilder);
        checkWebUrl();
    }
    // endregion

    // region 匹配相应字符进行高亮显示
    public void highLight(String txt, int color) {
        highLight(txt, 0, color);
    }

    public void highLight(String txt, int backgroundColor, int textColor) {
        highLight(txt, backgroundColor, textColor, false, null);
    }

    public void highLight(String txt, int backgroundColor, int textColor, boolean highLightAll,
                          OnClickRichTextListener onClickRichTextListener) {
        if (TextUtils.isEmpty(txt)) return;
        try {
            if (txt.contains("?"))
                txt = txt.replace("?", "\\?");
            SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(getText());
            Pattern pattern = Pattern.compile("(?i)" + txt);
            Matcher matcher = pattern.matcher(spannableStringBuilder);
            Object[] spans = null;
            if (enableParseRichText)
                spans = spannableStringBuilder.getSpans(0, spannableStringBuilder.length(), Object.class);
            boolean isContainInSpan; //找到的这个字符串位置是否已经是在自定义的span里面了
            while (matcher.find()) {
                int startIndex = matcher.start();
                int endIndex = matcher.end();
                isContainInSpan = false;
                if (spans != null && spans.length > 0)
                    for (Object span : spans) {
                        if (!(span instanceof NoUnderlineSpan) && !(span instanceof CustomUrlClickSpan) &&
                                !(span instanceof URLSpan) &&
                                spannableStringBuilder.getSpanStart(span) <= startIndex &&
                                spannableStringBuilder.getSpanEnd(span) >= endIndex) {
                            isContainInSpan = true;
                            break;
                        }
                    }
                if (!isContainInSpan) {
                    if (backgroundColor != 0)
                        spannableStringBuilder.setSpan(new BackgroundColorSpan(backgroundColor),
                                startIndex, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);


                    if (onClickRichTextListener != null) {
                        ClickableSpan clickableSpan = new ClickableSpan() {
                            @Override
                            public void updateDrawState(TextPaint ds) {
                                ds.setColor(textColor);
                            }

                            @Override
                            public void onClick(@NonNull View view) {
                                if (RichTextView.isLongClick()) {
                                    RichTextView.setIsLongClick(false);
                                    return;
                                }
                                onClickRichTextListener.onClick();
                            }
                        };
                        spannableStringBuilder.setSpan(clickableSpan, startIndex, endIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                    } else {
                        spannableStringBuilder.setSpan(new ForegroundColorSpan(textColor),
                                startIndex, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                    }
                }

                // 不是高亮全部，则处理完一个就直接退出
                if (!highLightAll) {
                    break;
                }
            }
            setText(spannableStringBuilder);
            checkWebUrl();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    // endregion

    private static class MyLinkedMovementMethod extends LinkMovementMethod {
        private static MyLinkedMovementMethod sInstance;

        public static MyLinkedMovementMethod getInstance() {
            if (sInstance == null)
                sInstance = new MyLinkedMovementMethod();
            return sInstance;
        }

        @Override
        public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
            // 因为TextView没有点击事件，所以点击TextView的非富文本时，super.onTouchEvent()返回false；
            // 此时可以让TextView的父容器执行点击事件；
            boolean isConsume = super.onTouchEvent(widget, buffer, event);
            if (!isConsume && event.getAction() == MotionEvent.ACTION_UP) {
                ViewParent parent = widget.getParent();
                if (parent instanceof ViewGroup) {
                    // 获取被点击控件的父容器，让父容器执行点击；
                    ((ViewGroup) parent).performClick();
                }
            }
            return isConsume;
        }
    }
}